home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-11-11 | 85.9 KB | 2,564 lines |
-
- Topics covered in this tutorial:
-
- * About this tutorial...
- * Running examples
- * Setting up your own Vocabulary Help
- * Setting up your own vocabulary
- * Using DOS/AREXX macros
- * Setting up HeliOS command macros
- * A useful command for use in macros: EDIT
- * Another useful command for use in macros: RUN
- * Using HeliOS_cmp
- * Getting user input
- * Setting up text and graphic output
- * A little more about HeliOS text streams
- * HeliOS Variables
- * HeliOS Constants
- * More about the concepts of "Run-time" and "Compile-time"
- * The Dictionary pointer
- * Using "CREATE"
- * Run-time initialisation of data structures within Dictionary memory
- * Easy ways of setting up text strings in HeliOS
- * The "Code Field Address", or "CFA"
- * Vectored Execution
- * Error handling routines
- * Using the Return stack
- * Using HeliOS includes
- * Calling Amiga libraries
- * Setting up loops in HeliOS
- * Counted Loops
- * Conditional Loops
- * Infinite Loops
-
-
- ----------------------
- About this tutorial...
- ----------------------
-
- By now, if you have followed all the preceding tutorial material, you
- should be fairly confident about writing small-scale HeliOS programs.
-
- Some people like to build upon their own ideas and go on from here to
- to construct their own programs, learning the language as they go.
-
- Other people like to work by copying and modifying existing programs,
- which is a also good way of learning more advanced techniques.
-
- As you will have seen, HeliOS provides the source code for several example
- programs, and it is a good idea to look at these to see how larger scale
- HeliOS programs can be constructed. Of course you may prefer to adopt a
- very different programming style: HeliOS is very flexible, and there are no
- fixed rules when it comes to program design.
-
- If you want to follow on from the example programs provided with HeliOS
- there is full source code available for complete large programs such as the
- HeliOS "Defender" arcade game.
-
- Whichever method of progress you choose, we cannot go too far along the
- learning path with you, for the simple reason that HeliOS is deliberately
- very flexible, and Amiga programming has so many facets that it would be
- quite impossible and undesirable to lay down fixed guidelines.
-
- Nevertheless, there are a few important techniques, ideas, and points of
- interest which we can describe here, which will perhaps help to speed up
- your progress in the early stages. This tutorial is devoted to a brief
- discussion of various topics which you will hopefully find interesting,
- and which you can build upon in your own work by reading the more detailed
- documentation in the "Dictionary.doc" and elsewhere.
-
- The sections of this tutorial have no particular natural order, and are
- presented as a mixed bag of stimulating ideas: if you yourself have further
- ideas which you think would benefit other HeliOS programmers, please let us
- know so that we can include them in future versions of the tutorials.
-
- Remember that the techniques and ideas mentioned here are only a selected
- few of the ways you can use HeliOS. It is a good idea to read the HeliOS
- "Dictionary.doc" file very carefully, since it describes many functions and
- methods which you will find useful in particular circumstances.
-
-
- ----------------
- Running examples
- ----------------
-
- This tutorial contains many shorts sections of example code.
-
- Remember that you can easily run these examples from the editor by simply
- highlighting them and pressing <Amiga-e>.
-
- Note that many short example phrases have associated comments to the right
- of them, often using a preceding "->" symbol.
-
- These "->" symbols, and the following comments, are merely intended to
- indicate what the code fragment is doing, and should not be included in
- highlighted sections of code to be run-tested: if you do try to run these
- comments, you will merely get an error.
-
-
- -----------------------------------
- Setting up your own Vocabulary Help
- -----------------------------------
-
- The Vocabulary Help system is intended to be easy for you to use yourself
- to create your own notes on any HeliOS function.
-
- Place the cursor over the word "OpenScreen" below and press <Ctrl-Help> to
- see the brief Vocabualry Help available on that HeliOS command word.
-
- OpenScreen
-
- Notice that only a stack diagram is given, with no descriptive notes: this
- is because the Vocabulary Help system is intended to give very short quick
- help to which you can easily add your own notes as required by editing the
- Vocabulary Help file in one of the HeliOS editors.
-
- Each individual has their own needs when it comes to help and reminders on
- various functions. It is really easy to add your own help notes, so do not
- hesitate to start making your own additions to the help file.
-
- As you will have seen, the Vocabulary Help file provided as a starting point
- merely displays a stack diagram, and this leaves plenty of spare space for
- any text comments you wish to add. The Vocabulary Help file itself contains
- instructions on how to add your own help text: just load this file into one
- of the HeliOS editors and read the instructions given in the file.
-
- The Vocabulary Help system is intended for brief quick "memory jogging"
- help, rather than detailed texts, and allows two short "pages" of help text
- which are quick to read and access: do not write an "essay" for Vocabulary
- Help, but give information which can be taken in at a glance.
-
- We recommend that you become familiar with this system early in your HeliOS
- programming so that you get maximum benefit during the learning period when
- help is most required. You will also find that the very act of creating
- your own help texts will in itself help you learn much relevant information.
-
-
- ------------------------------
- Setting up your own vocabulary
- ------------------------------
-
- You will probably soon find that you have created a nucleus of useful
- HeliOS functions which you like to use in all your programs, and you may
- even create separate "toolkits" of functions for different applications.
-
- You COULD manually load your special vocabulary each time you start HeliOS,
- or you COULD cut and paste this vocabulary into each of your program source
- code files, but this is rather tedious.
-
- It is easy, in fact, to include any special vocabularies by simply using
- "RUN" to load the relevant source code file at the start of your program.
-
- You can RUN as many separate external source code files as you wish from
- within any source code file, and you can also RUN source code files which
- themselves RUN other files: the system is very flexible and easy to use.
-
- Here is an example showing how to use RUN to compile an external source code
- file from within any program:
-
- RUN HeliOS:Source/MyOwnVocabulary.src
-
-
- The above line could be included anywhere in your source code, but would
- usually be placed immediately after the
-
- FORGET **CORE**
-
- line with which most programs begin.
-
-
- In fact the start of your source code is probably where you would want to
- initialise all of the general environment for your program, so you might
- have a typical program start something like this:
-
-
- FORGET **CORE**
-
- AMIGAINCLUDE HeliOS:HeliOS_AmigaInclude
- USERINCLUDE HeliOS:HeliOS_UserInclude
-
- DOSCMD $Assign MyProgram: HeliOS:MyProgram$
-
- RUN HeliOS:Source/MyOwnVocabulary.src
- RUN MyProgram:MyProgramToolkit.src
-
- etc. etc.
-
-
- ----------------------
- Using DOS/AREXX macros
- ----------------------
-
- You will have noticed that the HeliOS editors have a macro recording system
- which allows you to record DOS/AREXX macro command lines and execute them
- via hot-keys for easy and instantaneous operation.
-
- These macros can only be created in the editor environment, but you should
- be aware that they can be executed both in the editor AND the interpreter
- environments.
-
- This useful facility allows you to make use of instant hot-key DOS or AREXX
- commands in the interpreter as well as the editors.
-
- Note that ordinary editor macros cannot be run from the interpreter: only
- DOS/AREXX macros have this facility.
-
- See the "DOSandAREXX_Commands.doc" for more details
-
-
- --------------------------------
- Setting up HeliOS command macros
- --------------------------------
-
- You can attach a HeliOS command line to a hot-key, and this allows you to
- run any HeliOS program at the touch of a key.
-
- This can be useful if you wish to use a certain command sequence often: for
- example you may like to attach the "FORGET **CORE**" command to a hot-key.
-
- To do this you must create a special form of DOS/AREXX macro.
-
- If you want to set up a DOS/AREXX macro command line to be treated as a
- HeliOS command line you must place a special code at the start of the line.
-
- There are two possible codes, "H>" and "E>", which HeliOS interprets in
- slightly different ways.
-
- 1. Using H>
-
- Placing H> at the start of the DOS/AREXX command line will cause HeliOS to
- execute that line as HeliOS code in a full interpreter "session", just as
- if that line had been entered at the interpreter command line.
-
- For example, setting up this line as a macro:
-
- H> FORGET **CORE**
-
- will produce a macro command which behaves exactly as if you had typed
- "FORGET **CORE**" at the intepreter command line.
-
- 2. Using E>
-
- Placing E> at the start of the DOS/AREXX command line will cause HeliOS to
- execute that line as HeliOS code without registering a full interpreter
- "session".
-
- The code will simply be executed without all the extra business of the
- session log etc.
-
- For example, setting up this line as a macro:
-
- E> FORGET **CORE**
-
- will produce a macro command which executes apparently in the background,
- with no visible effect but still doing its job of clearing the dictionary.
-
- See the "HeliOS_Macro_Commands.doc" for more details.
-
-
- ----------------------------------------
- A useful command for use in macros: EDIT
- ----------------------------------------
-
- You might like to set up macros, as explained above, to edit certain files
- which you use very often. To do this you can use the very useful EDIT
- command, which allows you to auto-load any file into a HeliOS editor.
-
- Look at this macro command line, for example:
-
- E> 3 EDIT S:Startup-sequence
-
- This will load the "Startup-sequence" file into HeliOS Editor3.
-
- Here is how the command works:
-
- E> 3 EDIT S:Startup-sequence
- ^ ^^^^ ^^^^^^^^^^^^^^^^^^
- | | |
- | | File name
- | |
- | Edit command
- |
- Which editor you want the file to be loaded into
-
- If there is already unsaved text in the specified editor you will get a
- warning, giving you the opportunity to save the old text first.
-
-
- ---------------------------------------------
- Another useful command for use in macros: RUN
- ---------------------------------------------
-
- You might like to set up macros, as explained above, to run certain HeliOS
- programs, and to do this you can use the very useful RUN command.
-
- For example:
-
- F> RUN HeliOS:Source/MyUsefulProgram
-
- Using this method you could, for example, attach particular sets of your
- own "Toolkit" commands to each Function key, and thus you could customise
- your HeliOS environmemt at the touch of a key.
-
- The fact that you can attach literally ANY HeliOS program to a command key
- gives you limitless scope to customise your own programming environment.
-
-
- ----------------
- Using HeliOS_cmp
- ----------------
-
- The HeliOS_cmp program can be used as a straightforward stand-alone HeliOS
- compiler, or as a versatile method of testing any HeliOS program.
-
- The latter option is very useful provided that you observe the constraint
- that the full interpreter environment will not be available, so you cannot
- run software which uses facilities only present in the full interactive
- HeliOS system.
-
- Remember also that source code run from HeliOS_cmp will need to contain
- explicit commands to load whatever include files are required, whereas in
- the full interactive system include files are usually permanently on-line.
-
- HeliOS_cmp can be used in various different ways, and it is worth looking
- at these in this tutorial:
-
- 1. As a compiler of stand alone programs for use with HeliOS_exe.
-
- In this case you are interested in generating a compiled code file, but
- not a compiled vocabulary module as used in the full interpreter.
-
- You would use the "-v" command line option to prevent generation of a
- full vocabulary module.
-
- You would use a command line using "-v" something like this:
-
- HeliOS:HeliOS_cmp HeliOS:Source/MyProgram Ram:MyProgram -v
- ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^
- | | | |
- Compiler program Source code file Target file Specifies no
- (Optional) vocabulary module
-
- Note that if you do not specify a target file the compiled file will be
- given the same name and path as the source file with a ".cmp" suffix.
-
- The HeliOS_cmp program will open a 4-colour screen and present you with
- full compilation text output and error messages.
-
- In this case a final end-of-compilation message and free bytes statement
- will apppear.
-
-
- 2. As a compiler of programs to be "preloaded" into the interpreter.
-
- In this case you need to generate a compiled code file and a compiled
- vocabulary module to be used by the full interpreter.
-
- You would NOT use the "-v" command line option, but otherwise the command
- line format would be just like the previous case.
-
- HeliOS:HeliOS_cmp HeliOS:Source/MyProgram Ram:MyProgram
- ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
- | | |
- Compiler program Source code file Target file
- (Optional)
-
- Note that if you do not specify a target file the compiled file will be
- given the same name and path as the source file with a ".cmp" suffix.
-
- The vocabulary module will have the same name but with a "V" suffix.
-
- The HeliOS_cmp program will open a 4-colour screen and present you with
- full compilation text output and error messages.
-
- In this case a final end-of-compilation message and free bytes statement
- will apppear.
-
-
- 3. To test run programs without generating any output files
-
- In this case want to simply compile a program and you are only interested
- in seeing the program run, not in generating compiled code.
-
- To do this you specify the command line option "null", as follows:
-
- (Note that the "null" can be in upper case (e.g. "NULL") if you wish)
-
- HeliOS:HeliOS_cmp HeliOS:Source/MyProgram null
- ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^
- | | |
- Compiler program Source code file Specify NO output files
-
- In this case you will probably want a visual environment for your program
- as close as possible to the setup of the full HeliOS interpreter.
-
- The HeliOS_cmp program will try to simulate the interactive HeliOS system
- display by by opening an 8-colour screen and setting its colours to your
- existing saved HeliOS system default colours.
-
- With this option the final end-of-compilation message and the free bytes
- statement will not apppear.
-
-
- ------------------
- Getting user input
- ------------------
-
- The Amiga allows many methods of gathering user input, and most of them
- are rather complex to set up and administer.
-
- Probably the most common, flexible, and system friendly method is to use
- an Intuition IDCMP (Intuition Direct Communication Message Port) which
- will gather user input from an active Intuition window.
-
- This can still be a very tedious process, but you are quite at liberty
- to use this method within HeliOS if you wish.
-
- However, the good news is that HeliOS provides a sophisticated automatic
- IDCMP handler which is ridiculously easy to set up and use.
-
- You can read more about this in the dictionary documentation, but here
- we will give a simple description of how to set up the HeliOS IDCMP
- handler.
-
- First of all, you must understand that the HeliOS IDCMP handler can only
- be used in conjunction with an Intuition window, which is fairly obvious
- since it employs an Intuition IDCMP!
-
- Next you must remember that there can be only one HeliOS IDCMP handler
- in operation at any particular time.
-
- When HeliOS starts up it always opens an IDCMP handler and attaches this
- to the HeliOS startup window: you do not have to do anything at all to
- set up this initial facility.
-
- Subsequently you can reset the HeliOS IDCMP handler to get user input
- from any window which your program opens, and you can switch easily and
- quickly between as many different windows as you wish.
-
- Finally, when you have finished, you should return the IDCMP handler to
- the HeliOS window.
-
- You do not need to worry about finally closing the HeliOS IDCMP handler,
- because it will be closed automatically when HeliOS itself closes down.
-
- To set the HeliOS IDCMP handler to get input from a new window you have
- just opened, you would say something like:
-
- MyNewWindow D@ MAKEINWINDOW
-
- The simple MAKEINWINDOW command is all that is required to set up user
- input from any window.
-
- To restore user input to the main HeliOS window you should use the single
- word:
-
- FORTHINWINDOW
-
- Remember that you MUST transfer the HeliOS IDCMP handler back to the
- HeliOS window before closing the other window you have been using.
-
- It really is that simple to set up the HeliOS user input handler!
-
- Of course, having attached the HeliOS user input handler to a window, you
- still need to know how to check for user input.
-
- This is very easy indeed, and is most often done using the word "KEY",
- which will halt program execution and wait for ANY user input. As soon
- as KEY finds some user input to report it "wakes up" and returns a code
- on the stack defining just what happened. If it was a simple key press
- you will receive an ASCII code, if it was another input event such as a
- gadget you will get a special code informing you just what happened.
-
- If you need further information about the nature of the event, you can
- get more details by inspecting special system variables such as RAWKEY,
- QUALIFIER, GADGETNUMBER, MENUNUMBER etc..
-
- Of course, you may not want your program to go to sleep while waiting
- for user input, and in this case you can use the word "?TERMINAL", which
- tests for user input and returns at once with a user input code on the
- stack, or "null" if nothing is happening.
-
- It is important to become familiar with the use of MAKEINWINDOW, KEY,
- and ?TERMINAL right from the start of your HeliOS programming.
-
- This system is so easy to use that you will very soon become fluent in
- writing code using the simple input words described: there are more
- sophisticated tools available within HeliOS, but you will not need these
- to start with.
-
- Here is a short example which shows the use of KEY.
-
- : TESTKEY
-
- SCRCLR
-
- 5 4 CURPUT ." Press any key to see ASCII code returned by KEY ->"
- 5 6 CURPUT ." Press <Space> to quit"
-
- BEGIN
- KEY \ Get KEY
- 56 4 CURPUT \ Set cursor position
- 6 FPENSET \ Set colour to red
- DUP . \ Duplicate and print ASCII code
- 32 = \ Check if it was <Space>
- UNTIL
- SCRCLR
- ;
-
- TESTKEY
-
- See the "Dictionary.doc" and example programs for more details.
-
-
- ----------------------------------
- Setting up text and graphic output
- ----------------------------------
-
- In the previous section you saw how easy it was to set up user input
- within any window using MAKINWINDOW.
-
- The good news is that equally simple and rather similar methods are
- available for setting up text and graphic output in windows.
-
- To set up text output you use the function "MAKEOUTWINDOW", and to set
- up graphical output you use "MAKEGFXWINDOW".
-
- So, if you wanted to make a newly opened window active for user input,
- text output, and graphics output, you might say:
-
- MyNewWindow D@ MAKEGFXWINDOW
- MyNewWindow D@ MAKEOUTWINDOW
- MyNewWindow D@ MAKEINWINDOW
-
- Remember again that you MUST transfer the all HeliOS functions back to
- the HeliOS window before closing the other window you have been using.
-
- You saw in the previous section how to close user input by returning the
- HeliOS IDCMP handler to the HeliOS window, using "FORTHINWINDOW".
-
- The method of closing down text output is like that used to close the
- HeliOS IDCMP handler, except that you use the function "FORTHOUTWINDOW".
-
- The method of closing down graphic output is slightly different, and in
- this case you need to use two words:
-
- FWINDOW MAKEGFXWINDOW
-
- Once you have set up text and graphic output in your window you can use
- a whole range of convenient pre-coded HeliOS functions: HeliOS has many
- powerful text functions associated with its system of ten text streams
- and a complete set of graphic rendering functions.
-
- See the "Dictionary.doc" for more details.
-
-
- ---------------------------------------
- A little more about HeliOS text streams
- ---------------------------------------
-
- HeliOS text output, as you have seen in the earlier tutorials, is sent
- through a series of text streams which can be used to remember many
- characteristics of a text window.
-
- It is worth looking at the "Dictionary.doc" to see the wide range of text
- control functions available in HeliOS, but there are so many of these
- that you will probably find it easiest to learn them slowly as you need
- them.
-
- However, the use of the words STREAM and STREAMNO is worth describing at
- this point, because they are fundamental to using text streams.
-
- The word STREAMNO returns the number of the current text stream, and is
- very useful when you need to remember the old stream while jumping on
- a temporary basis to another stream.
-
- You can use code something like this:
-
-
- SCRCLR
-
- ." Here we are, starting off!"
-
- STREAMNO \ Get current stream number onto stack
-
- 8 STREAM \ Jump to new stream - number 8
-
- 10 10 60 12 SCRWIN
- 7 BPENSET 6 FPENSET
- SCRCLR
- ." Here we are in Stream 8!"
- CR
- CR
- ." We were in Stream " DUP . \ Duplicate original stream number in stack
- CR
- CR
- ." And now we are going back!"
-
- STREAM \ Jump back to original stream
-
- 1 20 CURPUT
-
- ." Now we are back!"
- CR
- CR
- ." Press <Space> to continue."
-
- WAITSPACE
-
- SCRCLR
-
-
- Try running this section of code.
-
-
- The word STREAM must be preceded by a stream number which must lie in
- the range 0-9 to select one of the ten HeliOS text streams (note that
- if you specify a number greater than "9" HeliOS will use a value of "9"
- internally by default).
-
- Note, by the way, that the text stream system is unique to HeliOS and is
- NOT part of the Amiga operating system: it USES the Amiga console device
- but it is not part of the standard Amiga console device functionality.
-
- Once you have defined the current text stream, HeliOS will allow you to
- set up a text window (using the SCRWIN function) and define text styles,
- cursor position etc..
-
- Then you can change streams and set up another text window with different
- styles etc.
-
- Subsequently, simply evoking one of your preset streams will restore all
- text parameters to the ones defined for that stream.
-
- Note that text stream windows are NOT the same as Intuition windows, and
- you can have ten different text stream windows within a single Intuition
- window.
-
- When organising text within a window it is often desirable to have some
- lines of text at odd pixel offsets from other text: in other words, to
- have text lines not dictated in terms of font dimension displacement.
-
- This is easy to set up with HeliOS streams, and you can easily have two
- adjacent text streams with a single pixel difference in either vertical
- or horizontal alignment if you wish.
-
- To change pixel alignment of a text window you use the "SETCONORIGIN"
- command, immediately before performing a SCRWIN operation. The SCRWIN
- function will then configure its window with respect to the new pixel
- origin.
-
- You should always use the FCONORIGIN function to reset the standard text
- origin after creating a special offset text window, and it is a good idea
- to do this immediately after the SCRWIN call.
-
- Remember in this context that the SETCONORIGIN command only affects the
- SCRWIN command, and after using SCRWIN you can freely use SETCONORIGIN
- without having any effect on text output until the next SCRWIN command.
-
- Notice also that the word STREAM itself uses SCRWIN to initiate the text
- window of a newly selected stream, so you must be careful not to leave
- the text origin set to some peculiar setting.
-
- * It is important to note that streams do NOT remember individual pixel
- origin settings.
-
- So, the best philosophy for using SETCONORIGIN is to use this function
- immediately before SCRWIN and then use FCONORIGIN immediately afterwards
- to revert to the standard origin settings.
-
- For example:
-
- 1 5 8 8 SETCONORIGIN
- 10 10 40 5 SCRWIN
- FCONORIGIN
- etc.
- etc.
-
- Note that the parameters to the SETCONORIGIN function have two distinct
- categories:
-
- 1. The origin pixel offsets (horizontal and vertical)
- 2. The font-related character multipliers (horizontal and vertical)
-
- The origin pixel offsets are simple additive offsets, specified as either
- positive or negative, and are used to fine-tune the final screen position
- of the text window.
-
- The character multipliers are pixel values by which the window top left
- corner character offsets specified in SCRWIN are multiplied to give the
- initial pixel position for the top left corner of the text window.
-
- Character multipliers relate to horizontal and vertical font resolution,
- and would normally each be set to 8 for a standard 8x8 pixel font.
-
- For example:
-
- 0 5 8 8 SETCONORIGIN
-
- sets the vertical pixel offset to 5, and the vertical multiplier to 8.
-
- If you now say:
-
- 20 12 40 8 SCRWIN
-
- you are asking for a text window which starts 12 text character rows
- from the top of the console window.
-
- Since you have specified the font "character multiplier" as "8", the
- first thing HeliOS will do to get the vertical window pixel displacement
- is to multiply the character row offset by the character multiplier to
- get an initial text window vertical pixel offset:
-
- 12 x 8 = 96 pixels
-
- Having arrived at this basic font-related overall offset, HeliOS applies
- the fine-tuning offset adjustment specified by the origin pixel offset.
-
- In our example this would mean adding 5 pixels to the vertical offset.
-
- 96 + 5 = 101 pixels
-
- Thus our text window would open 101 pixels from the top of the console
- window.
-
- Text window pixel origin offset settings can be determined and modified
- individually using the variables CONHOFFSET and CONVOFFSET, and the
- character multipliers can be determined and modified individually using
- the variables CONHSCALE and CONVSCALE.
-
- Note that simply storing a value in one of these variables is sufficient
- to set the origin for the next use of SCRWIN, and reading these variables
- will always give you the current text window origin setting.
-
- See the "Dictionary.doc" for more details.
-
-
- ----------------
- HeliOS Variables
- ----------------
-
- HeliOS, as you know, handles much of its parameter passing via the stack.
-
- Other languages make more use of named variables, and many programmers
- who are already experienced with, for example, BASIC, may feel initially
- uncomfortable when confronted with the stack-oriented HeliOS system.
-
- Actually you can, and indeed must, use named variables in HeliOS to an
- extensive degree, although in some cases this may be less efficient (and
- often more involved) than using the stack.
-
- In most cases local parameter passing is best implemented using the stack,
- but global parameters are generally stored as variables.
-
- In some languages variables are treated as more or less "abstract" entities
- with sometimes quite complex properties, restrictions, and error checking.
-
- In HeliOS, variables are very transparently and directly associated with the
- accessing of data stored directly in memory: they are in no way complex or
- mysterious, and have very practical useage. In HeliOS a variable is simply
- an allocated section of memory with which a name has been associated.
-
- Although many other languages may obscure the fact, the use of variables is
- really all about naming a memory location where you are storing data, and
- this simple scenario is exactly how you should regard HeliOS variables.
-
- After all, what the computer is really doing when you manipulate a variable
- is to move data in and out of a labelled storage location: even in languages
- which attempt to insulate (isolate) you from the reality of what the CPU is
- doing, the underlying action is fundamentally similar. The problem is that
- the more you are insulated from direct considerations of the computer's CPU,
- the less efficient will be the data handling of your programs.
-
- HeliOS variables can be in 3 data sizes (Byte, Word, and Long), and you can
- easily generate string variables which contain counted ASCII text strings.
-
- Calling a HeliOS variable results in an address being placed on the stack:
- remember that this is NOT the variable data value (the variable contents),
- but the address where that variable's data is stored.
-
- You can regard variables as a little like "mail boxes" in memory, and these
- "boxes" have very simple properties:
-
- 1. Each variable has a unique identifying address which can be either 16-bit
- or 32-bit, depending how you defined the variable.
-
- 2. Each variable has a space allocated for storing its contents, and this
- data space can be either 1 Byte (8-bits), 1 Word (16-bits), or 1 LongWord
- (32-bits) in length for normal data variables.
-
- The "length" of string variables obviously depends on the length of the
- string itself.
-
- 3. Each variable has a name
-
- HeliOS variables are really very simple indeed, and have absolutely no
- constraints or checks whatever on useage.
-
- Variables are not "typed" in HeliOS, which is to say that the data stored
- in any numeric variable can be whatever you choose.
-
- Furthermore, HeliOS does no checking whatever on what you are doing with
- the contents of a variable: if you are silly enough to use the contents of
- a pure data variable as an address by mistake, HeliOS will let you go ahead
- and do it! It also is up to you to keep track of what size a variable is,
- and HeliOS will not check to see if you are trying to store a longword value
- into a byte sized variable.
-
- In other words you have total freedom (and therefore speed and efficiency)
- in handling variables, but it is entirely your own responsibility to ensure
- that you get everything correct. Most programmers would agree that this
- speed and freedom is ideal, but it may come as a shock to programmers who
- have been used to somewhat more restrictive systems.
-
- In this respect, as in many others, HeliOS is more like assembly language
- than other high level languages.
-
-
- When a HeliOS data variable is defined, the following need to be specified:
-
- 1. The initial data value
-
- 2. The data size (Byte, Word, or Long)
-
- 3. The addressing size (16-bit or 32-bit)
-
- 4. The variable's name
-
-
- This process of variable definition is very easy indeed.
-
- Here are a few examples, creating a variable called "PIGLETS":
-
- 123 VARIABLE PIGLETS -> 16-bit data, initial value 123, 16-bit addressed
-
- 123. DVARIABLE PIGLETS -> 32-bit data, initial value 123, 16-bit addressed
- ^
- Specifies 32-bit data
-
- 123 VARIABLEL PIGLETS -> 16-bit data, initial value 123, 32-bit addressed
- ^
- Specifies 32-bit addressing
-
- 123 CVARIABLE PIGLETS -> 8-bit data, initial value 123, 16-bit addressed
- ^
- Specifies 8-bit data
-
- 123. DVARIABLEL PIGLETS -> 32-bit data, initial value 123, 32-bit addressed
-
-
- After this initial specification, whenever you use the variable's name you
- will get on the stack the address (either 16-bit or 32-bit, depending on
- how the variable was defined) where the variable's contents are stored.
-
- For example, let us assume that you want to create a normal 16-bit addressed
- variable called APPLES, with a value of "10" stored in it.
-
- You could define such a variable like this:
-
- 10 VARIABLE APPLES
-
- Note that you always have to supply an initial value when you first create
- a HeliOS variable, and saying simply
-
- VARIABLE APPLES
-
- will result in an error.
-
- If you now say, in your program code or the command line, the word APPLES,
- HeliOS will place on the stack the 16-bit variable address: this address
- is the place in memory where the number 10 is at present stored.
-
- Having got the variable address on the stack, you can either change the
- value of the data stored there, or read the old data contents.
-
- To change the contents of a variable, you might say
-
- 15 APPLES ! -> Stores a new value of "15" into APPLES
-
- To get the current contents of a variable, you might say
-
- APPLES @ -> Gets the current value of APPLES onto the stack
-
- Incrementing and decrementing variables is very simple:
-
- APPLES INC -> Increment the APPLES variable
-
- APPLES DEC -> Decrement the APPLES variable
-
- Moving data between variables is easy too:
-
- APPLES FRUIT MOVE -> Move the data contents of APPLES into FRUIT
-
- There are various words for getting and storing values in variables, and
- in general these functions are exactly the same functions which you use in
- ordinary memory accessing operations.
-
- In fact, always remember that a variable IS just a neatly labelled section
- of memory as far as HeliOS is concerned.
-
- See the "Dictionary.doc" section on "MEMORY OPERATIONS" for a full list of
- all the many powerful ways of accessing variables and memory in HeliOS.
-
-
- ----------------
- HeliOS Constants
- ----------------
-
- HeliOS Constants are even easier to use than variables, and are defined in
- a very similar way by specifying:
-
- 1. The initial data value
-
- 2. The data size (Word, or Long)
-
- 3. The constant's name
-
- Note that because HeliOS does not support byte sized stack or dictionary
- cells, it would be pointless to have a byte sized constant.
-
- Such a constant would need to be stored in 2 bytes of memory space and
- would require a 2 byte stack cell, thus effectively being no different
- from a normal 16-bit constant.
-
-
- Here are examples of constant definitions:
-
- 123 CONSTANT PIGLETS -> 16-bit data, initial value 123
-
- 123. DCONSTANT PIGLETS -> 32-bit data, initial value 123
- ^
- Specifies 32-bit data
-
- Once defined, when the constant's name is used, the actual data value of
- the constant will be placed on the stack.
-
-
- --------------------------------------------------------
- More about the concepts of "Run-time" and "Compile-time"
- --------------------------------------------------------
-
- HeliOS is both a compiler and an interpreter, and in many cases the same
- functional "words" are used by HeliOS in both modes of operation.
-
- In fact "compilation" is actually performed by the "interpreter", so you
- could perhaps say that during compilation a dual process of compilation and
- interpretation is taking place. This introduces a "dual-time" concept with
- respect to any program, because there will be some words specified within
- the program source code which are executed WHEN COMPILING, and others which
- only execute WHEN THE PROGRAM IS RUN later.
-
- In general we use two terms to distinguish between the execution times of
- HeliOS functions:
-
- 1. COMPILE-TIME
-
- When a word executes while HeliOS is compiling a program, this is said
- to be "COMPILE-TIME" execution for that word.
-
- An example of this is the word ":" with which you begin word definitions
- in your source code. When HeliOS is compiling your program, it reads
- the ":" and immediately (at COMPILE-TIME) executes that word in order to
- initiate the colon definition.
-
- 2. RUN-TIME
-
- Words specified within colon definitions are not executed as the program
- is compiled, but only later when that defined word is run either in the
- program itself or from the command line.
-
- As HeliOS at compile-time reads word names specified within a colon
- definition, it will simply compile them. The functions thus compiled
- will not be executed until later at RUN-TIME when the program is actually
- running.
-
- When command line input is being interpreted directly, even though it is
- not really the case that a specific program is being compiled, there still
- exist the distinctions between run-time and compile-time.
-
- In most cases at the command line words are executed at once, so the moment
- when the command line is actually executed is RUN-TIME for these words.
-
- However, if the command line contains a colon definition, the words which
- are compiled within that definition will have their COMPILE-TIME as the
- command line is executed.
-
- Here we only want to introduce you to the general concepts of run-time and
- compile-time: elsewhere in the tutorials we give specific instances where
- it is vital to understand the distinction between functions being executed
- at compile-time and run-time.
-
- We will just mention here that HeliOS has many functions which allow you to
- control completely the action of all words at run-time and compile-time,
- allowing you to customise the process of compilation as required.
-
- One other interesting and important feature of certain special HeliOS words
- is that they can automatically "sense" whether they are in "compile mode"
- within a colon definition, or are being executed directly, and change their
- behaviour accordingly.
-
- These words are very useful, and are called STATE-SENSITIVE functions.
-
- The name "state-sensitive" is used because HeliOS, like FORTH, uses an
- internal system variable called "STATE" to set whether the HeliOS system
- is in COMPILE-MODE or EXECUTE-MODE. You can get the value of this STATE
- variable (using the expression "STATE @L") and use this in conjunction with
- other special functions to generate your own special state sensitive words.
-
- If you are brave, that is......
-
-
- ----------------------
- The Dictionary pointer
- ----------------------
-
- The HeliOS Dictionary memory space always has a current "top" position
- pointer which specifies the next free memory location in the Dictionary.
-
- As the Dictionary is used this "Dictionary Pointer" is gradually advanced
- until finally the Dictionary space is full.
-
- The current Dictionary Pointer is stored in a HeliOS system variable called
- "DP" and is always available by simply using the expression:
-
- DP @L
-
- Because this is quite a commonly used function, HeliOS provides a single
- word to access the current Dictionary Pointer: this word is "HERE".
-
- So
-
- HERE
-
- is directly equivalent to
-
- DP @L
-
- and both return the current first free cell in Dictionary memory space.
-
-
- --------------
- Using "CREATE"
- --------------
-
- If you wish to create data tables, string buffers etc. within the HeliOS
- dictionary, it is very easy and convenient to use CREATE or CREATEL.
-
- What happens when you use these functions is that you create a new word
- which when executed will return the address of the next dictionary space.
-
- For example, you might use:
-
- CREATE MyTable ; Create new word
- 1 , ; Start of data table
- 2 ,
- 3 ,
- 4 ,
-
- When you subsequently use the word "MyTable", the 16-bit dictionary address
- of the stored value "1" (the first location of the allotted table), will
- be returned.
-
- Note here the use of the simple and convenient word "," which simply stores
- a data value in the next free location within the HeliOS Dictionary memory
- space, then advances the Dictionary Pointer to the next position.
-
-
- Notice that CREATE can be used to make a standard variable, like this:
-
- CREATE MyVariableName
-
- 0 ,
-
- which is equivalent to
-
- 0 VARIABLE MyVariableName
-
-
- Here are a few useful points to note about CREATE.
-
- 1. The new word specified using CREATE will always return a pointer to the
- current next free Dictionary space when CREATE was used.
-
- 2. The words "ALLOT" and "," (and related functions) can be used after
- CREATE to allocate Dictionary memory and advance the Dictionary Pointer,
- thus effectively allowing the construction of convenient "protected"
- data space.
-
- 3. After using CREATE, the word HERE will return a pointer to the Dictionary
- address which will be returned by the word just created by CREATE.
-
- 4. Using CREATE only sets up a new word name header which when executed will
- return a pointer to the current Dictionary top free space.
-
- 5. Using CREATE does NOT allocate any Dictionary space or perform any other
- function to "protect" or affect in any way the Dictionary address which
- will be returned by the newly CREATED word.
-
-
- Another example of using CREATE might be:
-
- CREATEL MyName $ This is my name$
-
- Using the word "MyName" will now return a 32-bit pointer to the string "This
- is my name".
-
-
- Another example might be:
-
- CREATEL MyNameTable
-
- $ My name 1$
- $ My name 2$
- $ My name 3$
- $ My name 4$
-
- You can see what this does.....(It creates a 32-bit addressed string array)
-
- Of course you can also use VARIABLE etc. to do similar things, but the use
- of CREATE is very convenient and worth adding to your repertoire.
-
- Remember that although CREATE is very convenient to use, it does have the
- disadvantage that data structures created using this method use precious
- Dictionary space.
-
- This does not matter in small programs, but you must be careful not to use
- CREATE too liberally for large data structures or in very large programs.
-
- See the next section for another reason to take care when creating data
- structures in the Dictionary memory area.......
-
-
- -------------------------------------------------------------------
- Run-time initialisation of data structures within Dictionary memory
- -------------------------------------------------------------------
-
- It is very important to understand that any 32-bit pointers referenced
- within the HeliOS Dictionary must always be initialised in run-time code.
-
- Look at this a fragment of HeliOS code:
-
- CREATEL MyDataStructure1
-
- 0. D, \ <---------------------------
- 1 , |
- 2 , |
- 3 , | Points directly to previous structure
- |
- CREATEL MyDataStructure2 |
- |
- MyDataStructure1 D, \ --->----------
- 1 ,
- 2 ,
- 3 ,
-
- ( Notice that the above code is not within a colon definition, and will
- execute as the program is initially compiled )
-
- : MyProgram
-
- MyProgram1
- MyProgram2
- MyProgram3
- etc.
- etc.
- ;
-
- MyProgram
-
- Here you can see that the first field of MyDataStructure2 contains a
- 32-bit pointer to the first data structure, MyDataStructure1.
-
- Obviously this pointer is valid, and if you went on to compile and run the
- program containing this fragment, the pointer would work correctly: we have
- literally "defined" the first field of the second data structure to point
- to the first structure.
-
- No problem!
-
- Or is there......?
-
- There is a trap hidden here, which you must be very wary of.
-
- If you were to save the compiled program as a module to be used with the
- HeliOS executive program, you would indeed have a very bad problem.
-
- The problem is that the "MyDataStructure1" pointer would actually only have
- been initialised in the code fragment you saw above, which would execute at
- COMPILE-TIME (when the program was compiled before being saved as a HeliOS
- code module).
-
- Note that this pointer is an absolute 32-bit address, and would be valid
- specifically and only for the position in memory where the software was
- currently loaded (at compile-time).
-
- If the code module is later loaded into a HeliOS system which has been
- loaded into a different memory address (99% certainly the case!), notice
- that this absolute 32-bit pointer will be incorrect! The pointer was only
- valid in the initial "compile" environment, and will no longer be valid in
- the subsequent "execution" environment.
-
- In other words, absolute pointers in HeliOS code modules are NOT RELOCATED
- when a code module is reloaded by another HeliOS system.
-
- This means that you MUST ensure that all 32-bit pointers are initialised
- dynamically in RUN-TIME code.
-
- Thus, in the startup code of your actual program, within a colon definition
- WHICH WILL EXECUTE AT RUN-TIME, you must include a fragment of code like
- this:
-
- CREATEL MyDataStructure1
-
- 0. D,
- 1 ,
- 2 ,
- 3 ,
-
- CREATEL MyDataStructure2
-
- MyDataStructure1 D,
- 1 ,
- 2 ,
- 3 ,
-
- : InitMyDataStructure
-
- MyDataStructure1 MyDataStructure2 D!L
- ;
-
- : MyProgram
-
- InitMyDataStructure
-
- MyProgram1
- MyProgram2
- MyProgram3
- etc.
- etc.
- ;
-
- MyProgram
-
- Note the simple way that InitMyDataStructure is used to initialise the
- pointer: MyDataStructure1 returns a 32-bit pointer to MyDataStructure1 at
- run-time, and we simply use D!L to set this address into the first field
- of MyDataStructure2.
-
- So, an important "golden rule" is:
-
- * ALWAYS INITIALISE ALL POINTERS AND VARIABLES DYNAMICALLY IN RUN-TIME CODE
-
-
- There are two examples in the "HeliOS:Source" directory which demonstrate
- the use of run-time initialisation of linked data structures in Dictionary
- memory: you might like to look at these examples.
-
- These are the files "IntuitionGadgets.src" and "IntuitionMenus.src", where
- typical Intuition linked data structures for gadgets and menus are created
- within the HeliOS Dictionary space.
-
-
- ----------------------------------------------
- Easy ways of setting up text strings in HeliOS
- ----------------------------------------------
-
- It is most convenient, unless Dictionary space is in short supply, to set
- up text strings within the HeliOS Dictionary memory space.
-
- There are various ways of doing this, and below we give examples of some of
- the easiest and most commonly used.
-
-
- Strings which return 16-bit addresses:
-
-
- 20 $VARIABLE MyString
-
- -> Allocates space for a 20 character counted string, initialised to null.
-
-
- $CONSTANT MyString $Contents of MyString$
-
- -> Creates a counted string with contents "Contents of MyString".
-
-
- CREATE MyString $ $Contents of MyString$
-
- -> Creates a counted string with contents "Contents of MyString".
-
-
- CREATE MyString <$ $Contents of MyString$
-
- -> Creates an uncounted string with contents "Contents of MyString".
-
-
-
- Strings which return 32-bit addresses:
-
-
- 20 $VARIABLEL MyString
-
- -> Allocates space for a 20 character counted string, initialised to null.
-
-
- $CONSTANTL MyString $Contents of MyString$
-
- -> Creates a counted string with contents "Contents of MyString".
-
-
- CREATEL MyString $ $Contents of MyString$
-
- -> Creates a counted string with contents "Contents of MyString".
-
-
- CREATEL MyString <$ $Contents of MyString$
-
- -> Creates an uncounted string with contents "Contents of MyString".
-
-
- If you simply want a string when typing at the command line it is easiest
- to use the word $NEXTL as follows:
-
- $NEXTL Contents_of_MyString
-
- Notice that this word is quick and easy, but has the disadvantage that
- it requires that the string has no internal spaces: in fact $NEXTL will
- simply get the next word in the input stream delimited by spaces.
-
- If you want a string which includes spaces when typing at the command line
- it is easiest to use $DELNEXTL which uses the system string delimiter:
-
- $DELNEXTL $Contents of MyString$
-
- Both these words return counted strings, and if you require an uncounted
- string it is easiest to simply increment the counted string pointer by one
- to skip the count byte.
-
- Both these words return PADL, where the string has been stored, so if you
- want to preserve the string for future use you must move it from PADL to
- somewhere safer, because PAD is used internally by HeliOS in many of its
- string manipulations.
-
-
- ----------------------------------
- The "Code Field Address", or "CFA"
- ----------------------------------
-
- This is part of the internal working of HeliOS which is very useful for you
- to know about, as you will see later.
-
- Originally, in FORTH, all words had the following sections:
-
- Name Field Address (= "NFA") -> The address of the word name itself
-
- Code Field Address (= "CFA") -> The address of the word's execution
- code
-
- Parameter Field Address (= "PFA") -> The address of the word's "body"
-
- Details of how these internals work need not worry you, especially since
- HeliOS is no longer internally constructed in the same way as FORTH, but
- you will from time to time see references to these names in documentation.
-
- In HeliOS, unlike FORTH, the various parts of any word are not stored in
- the same memory area, and although HeliOS words do still have NFA, PFA and
- CFA fields, these are not strictly the same as the old FORTH concepts.
-
- The really useful thing in all this is the "CFA", because you can make use
- of this address to EXECUTE a command indirectly.
-
- You do not need to know details of internal working, but you do need to
- know two important things:
-
- 1. How to get the CFA of any word
-
- 2. How to use the CFA to execute a word indirectly
-
-
- To get the CFA of any word, you can use these HeliOS functions:
-
- FIND ( _ _ _ CFA or 0 )
-
- Used in the form:
-
- FIND MyWord
-
- to search the dictionary for "MyWord".
-
- If not found flag 0 is returned, otherwise the CFA of
- MyWord is returned.
-
- This is a state sensitive word and performs its function
- within a colon definition by compiling the found CFA as
- a literal.
-
- LATESTCFA ( _ _ _ CFA )
-
- Returns CFA of last word in current vocabulary.
-
- MYSELF ( _ _ _ CFA )
-
- Used within colon definitions to compile the CFA of the
- word being defined for recursive functions.
-
- ' ( _ _ _ PFA ) "tick"
-
- Used in the form:
-
- ' MyWord
-
- where MyWord is any word in the dictionary.
-
- Searches dictionary for MyWord and if found returns PFA.
-
- If not found an error is reported.
-
- This is a state sensitive word and performs its function
- within a colon definition by compiling the found PFA as
- a literal.
-
- CFA ( PFA _ _ _ CFA )
-
- Converts HeliOS word PFA on stack to CFA.
-
- Obviously you can only get the CFA of a word after it has been defined, but
- the special case of MYSELF is interesting in that it allows you to obtain
- a reference to the CFA of the current word being compiled, thus giving the
- facility for employing recursion (this is an advanced operation and is only
- mentioned here for the sake of interest).
-
-
- Important note:
-
- There is a VERY IMPORTANT proviso which you must remember when getting the
- CFA of any HeliOS word:
-
- * YOU CAN ONLY FIND THE CFA OF A NAMED HELIOS FUNCTION IF THE VOCABULARY
- INFORMATION IS PRESENT
-
- This means that:
-
- * ALL "CFA FINDING" OPERATIONS MUST BE PERFORMED BY COMPILE-TIME CODE AND
- NEVER BY RUN-TIME CODE
-
- This is a subtle but vitally important point and needs further explanation:
-
- When a program is being compiled or interpereted the HeliOS system keeps on
- line a vocabulary word name list so it can locate any specified word.
-
- The functions "'" and "FIND" make use of this vocubulary name listing to
- find the PFA or CFA of a word at compile time.
-
- If you are subsequently running a program using the stand-alone HeliOS
- executive module, the vocabulary name list is no longer present, so your
- program code cannot use "'", or "FIND" functions to get a word CFA.
-
- This means that you must perform all CFA finding operations in code which
- runs at COMPILE-TIME, as the program is actually being compiled, when the
- full vocabulary information is present.
-
- The best way of doing this is to find all required CFAs at compile time and
- store them into variables which can be accessed at RUN-TIME.
-
- For example:
-
- 0 VARIABLE (FUNCTION1) <- COMPILE-TIME code
- 0 VARIABLE (FUNCTION2) <- COMPILE-TIME code
- 0 VARIABLE (FUNCTION3) <- COMPILE-TIME code
- etc.
- etc.
-
- : FUNCTION1 PLACE YOUR PROGRAM CODE HERE ;
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- RUN-TIME code
- : FUNCTION2 PLACE YOUR PROGRAM CODE HERE ;
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- RUN-TIME code
- : FUNCTION3 PLACE YOUR PROGRAM CODE HERE ;
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- RUN-TIME code
- etc.
- etc.
-
- FIND FUNCTION1 (FUNCTION1) ! <- COMPILE-TIME code
- FIND FUNCTION2 (FUNCTION2) ! <- COMPILE-TIME code
- FIND FUNCTION3 (FUNCTION3) ! <- COMPILE-TIME code
- etc.
- etc.
-
- Remember:
-
- * NO OPERATIONS WHICH REQUIRE ACCESS TO THE VOCABULARY LIST ARE POSSIBLE
- WITHIN "RUN-TIME" CODE
-
-
- Having acquired the CFA of a word, you can use the following HeliOS words
- to execute the word:
-
- EXECUTE ( CFA _ _ _ )
-
- Executes word whose CFA is on stack.
-
- @EXECUTE ( a1 _ _ _ )
-
- Executes word whose CFA is stored at a1.
-
- This facility for indirect execution has many uses, the most important of
- which, as we will see next, is "Vectored Execution".
-
-
- ------------------
- Vectored Execution
- ------------------
-
- This is a very important technique which you will certainly need to use
- often in your HeliOS programs.
-
- The term "Vectored Execution" refers to the technique of executing HeliOS
- functions by reference to indirect pointers. The idea here is that you can
- store the CFA of a word in a variable, or a table, and execute the word by
- referencing the stored CFA.
-
- Here are a few a simple ways you could use this.
-
- 1. Changing the operation of an already compiled word.
-
- You could make word called PrintName as follows:
-
- 0 VARIABLE CurrentName \ Set up a dummy variable
-
- : PrintName CurrentName @EXECUTE ; \ Create command word
-
- : Name1 ." Name1" ; \ Create Name1
-
- LATESTCFA CurrentName ! \ Store CFA of Name1 as CurrentName
-
- This will give the function "PrintName" the action of printing "Name1", and
- PrintName can be incorporated in whatever routine you wish. At any time
- later you can change the operation of PrintName very easily: for example,
- you could change it like this:
-
- : Name2 ." Name2" ; \ Create Name2
- : Name3 ." Name3" ; \ Create Name3
- : Name4 ." Name4" ; \ Create Name4
-
- FIND Name2 CurrentName ! \ Store CFA of Name2 as CurrentName
-
- After this PrintName would print "Name2".
-
- or
-
- FIND Name4 CurrentName ! \ Store CFA of Name4 as CurrentName
-
- After this PrintName would print "Name4".
-
- etc.
-
-
- 2. Using selective execution tables.
-
- You can conditionally execute one of a set of functions by reference to a
- table.
-
- Here is an example:
-
- : Name1 ." Name1" ; \ Create Name1
- : Name2 ." Name2" ; \ Create Name2
- : Name3 ." Name3" ; \ Create Name3
- : Name4 ." Name4" ; \ Create Name4
- : Name5 ." Name5" ; \ Create Name5
-
- CREATE MyNameTable
-
- FIND Name1 , \ Store CFA of Name1
- FIND Name2 , \ Store CFA of Name2
- FIND Name3 , \ Store CFA of Name3
- FIND Name4 , \ Store CFA of Name4
- FIND Name5 , \ Store CFA of Name5
-
- 0 VARIABLE CurrentFunctionNumber
-
- : GetName MyNameTable CurrentFunctionNumber @ 2* + @EXECUTE ;
-
- In this example, "GetName" will have its operation controlled by the number
- stored in the variable "CurrentFunctionNumber".
-
- Note that the value of CurrentFunctionNumber has been doubled (using 2*) in
- the above code, because each entry in the CFA table is 2 bytes in length,
- so all offsets into the table must be in multiples of 2 bytes.
-
- This technique of executing CFAs from a table can be used for conditional
- execution in many circumstances, and in general is much more powerful than
- using lots of nested conditional IF...THEN statements to carry out actions
- based on the value of a variable.
-
-
- 3. Using words before they have been defined.
-
- This is the most important use of vectored execution, and is an absolutely
- vital technique which you will often need in your HeliOS programs.
-
- Normally you can only use a word AFTER it has been created, and this often
- imposes quite severe limitations on the way you organise your code. This
- in itself is not too bad, but there can be occasions where you need to have
- functions which refer to each other: this is obviously impossible because
- the first word created cannot call the second word because it has not yet
- been defined.
-
- This is where vectored execution can be useful, because it allows you to
- define a set of executable variables at the start of a program which can
- be referred to by intermediate functions and then set up at the end of the
- program to contain the CFAs of any intermediate HeliOS words.
-
- Here is a trivial example to illustrate the general idea: you will find
- more complex examples in the source code example files provided.
-
- 0 VARIABLE (MyFunction1)
- 0 VARIABLE (MyFunction2)
- 0 VARIABLE (MyFunction3)
-
- : MyFunction1 (MyFunction2) @EXECUTE ;
- : MyFunction2 (MyFunction3) @EXECUTE ;
- : MyFunction3 ." Hello" ;
-
- FIND MyFunction1 (MyFunction1) !
- FIND MyFunction2 (MyFunction2) !
- FIND MyFunction3 (MyFunction3) !
-
- Notice that we used brackets surrounding the function names for the CFA
- storage variables. This is merely a matter of personal choice, but it is
- quite a useful idea to use some such consistent naming convention.
-
-
- -----------------------
- Error handling routines
- -----------------------
-
- It is useful to have a consistent method of dealing with program errors.
-
- You can easily create a custom error handler which prints an error message
- and closes down your program in a civilised fashion, deallocating all the
- resources which may have been opened before the error occurred.
-
- A good error handler allows all errors to be routed via a comprehensive
- sequential closedown routine, and this can be done easily using the HeliOS
- system customisable error handler word ERROR", which can be customised
- using SETERROR".
-
- When ERROR" senses an error it prints an associated error text message
- (which it gets from the next part of the input stream delimited by a '"'
- character) and then ABORTS program execution.
-
- This is OK as far as it goes, but it does not close down specific things
- related to your program, so you need to customise ERROR" to do a little
- more intelligent work.
-
- To do this you must first create a function which closes down everything
- your program opened: the error handler will use this function to end your
- program tidily.
-
- If you write such a function (called CLOSEDOWN for ease of reference here)
- you must make it intelligent enough to do two important things:
-
- 1. Close down everything in the exact reverse order in which it was created
- to help avoid memory fragmentation problems.
-
- 2. Check to see what has or has not yet been opened and only close down
- what is required. This is easy to do by having variables which are only
- set when an associated resource has been opened: for example, you could
- have a window handle variable which would be null until you filled it in
- when the window was actually opened.
-
- 3. It is a good idea if the CLOSEDOWN routine finally resets ERROR" to the
- standard system function, using RESETERROR".
-
-
- Here is a short general example of setting up a custom error handler.
-
- 0. DVARIABLE MYSCREEN
- 0. DVARIABLE MYWINDOW
-
- 0 VARIABLE (CLOSEDOWN)
- 0 VARIABLE (MYCLOSEDOWNERROR)
-
- : MYCLOSEDOWNERROR \ Action on error
-
- IF \ If there is an error
- CR \ Move down
- CR
- TYPE \ Type error message
- CR \ Move down
- CR
- ." Press <Space> to quit!" \ Print message
- CR \ Move down
- CR
- WAITSPACE \ Wait for <SPACE>
- (CLOSEDOWN) @EXECUTE \ Execute close down
- QUIT \ QUIT back to HelioS
- ELSE
- DDROP \ No error, so just DDROP
- THEN
- ;
-
- : CLOSEDOWN \ Closes down resources
-
- MYWINDOW D@
- D0<> \ ? Close window
- IF
- MYWINDOW D@ CLOSEWINDOW
- THEN
-
- MYSCREEN D@ \ ? Close screen
- D0<>
- IF
- MYSCREEN D@ CLOSESCREEN
- THEN
-
- RESETERROR" \ Reset system ERROR"
- ;
-
- FIND CLOSEDOWN (CLOSEDOWN) ! \ Set execution variable
- FIND MYCLOSEDOWNERROR (MYCLOSEDOWNERROR) ! \ Set execution variable
-
- : MYPROGRAM
-
- (MYCLOSEDOWNERROR) SETERROR" \ Set custom ERROR"
-
- LIT$ $MyScreen$ 640 256 4 OPENSCREEN \ Try to open screen
-
- DFLAG0= ERROR" Failed to open screen!" \ Test for error
-
- SCREEN1 D! \ Success, so store
-
- etc. etc.
- ;
-
- See the "Dictionary.doc" for full details of all related functions:
-
- ERROR", ERROR"FUNCTION, RESETERROR", SETERROR".
-
-
- ----------------------
- Using the Return stack
- ----------------------
-
- HeliOS, like FORTH, has a secondary stack which is used internally by the
- system for various purposes: this is called the "Return" stack because one
- of its main functions is to store return pointers during nested command
- threading operations.
-
- The Return stack will be familiar to FORTH users, but remember that HeliOS
- uses the Return stack differently in its internal operation, so do not rely
- on being able to do the same "tricks" with the Return stack that you might
- do in traditional FORTH.
-
- The Return stack can be used as a fast auxiliary data storage space for
- "local" data during the execution of a colon definition. However, you
- MUST ensure that the state of the Return stack is left unchanged overall,
- and that it is restored to exactly the same status at the end of any colon
- definition function as it had when the function started.
-
- There are easy to use and very fast words for moving data between the HeliOS
- stack and the Return stack, in both directions.
-
- The Return stack also has the useful property that within loop constructs it
- stores the loop index and loop limit: this means that you can use the Return
- stack to get loop indices and generally manipulate loops in special ways.
-
- The use of the Return stack in HeliOS is very similar to FORTH from the
- programmers point of view, and in many ways HeliOS seems to be using the
- Return stack exactly like FORTH. However, HeliOS does use the Return stack
- internally in special ways, so do not assume that standard FORTH usage is
- always applicable unless specifically stated.
-
- There are several operators which allow you to access numbers on the Return
- stack and to move numbers between the Return stack and the HeliOS stack:
-
- >R ( n1 _ _ ) Move top of stack onto return stack.
-
- D>R ( d1 _ _ ) Move top of stack d-number onto return stack.
-
- DDUP>R ( d1 _ _ d1 ) DDUP and move tos d-number onto return stack.
-
- DI ( _ _ d1 ) Get copy of top d-number from return stack.
-
- DI' ( _ _ d1 ) Get copy ofsecond d-number on return stack.
-
- DJ ( _ _ d1 ) Get copy of third d-number on return stack.
-
- DJ' ( _ _ d1 ) Get copy of fourth d-number on return stack.
-
- DR> ( _ _ d1 ) Remove top r-stack d-number onto HeliOS stack.
-
- DUP>R ( n1 _ _ n1 ) DUP and move top of stack onto return stack.
-
- I ( _ _ n1 ) Get copy of top number on return stack.
-
- I' ( _ _ n1 ) Get copy of second number on return stack.
- ( = 1st loop limit. )
- J ( _ _ n1 ) Get copy of third number on return stack.
- ( = 2nd loop index. )
- J' ( _ _ n1 ) Get copy of fourth number on return stack.
- ( = 2nd loop limit. )
- K ( _ _ n1 ) Get copy of fifth number on return stack.
- ( = 3rd loop index. )
- K' ( _ _ n1 ) Get copy of sixth number on return stack.
- ( = 3rd loop limit. )
- R> ( _ _ n1 ) Remove top of return stack onto HeliOS stack.
-
- R! ( l1 _ _ ) Sets Return stack pointer to l1.
-
- RP! ( _ _ ) Initialise return stack.
-
- RP@ ( _ _ l1 ) Return address of current return stack top.
-
-
- The most useful feature of the Return stack is that it can be used as a
- temporary store within the execution of a colon definition.
-
- If your stack parameter manipulations are getting complicated it is very
- useful to be able to move numbers off the HeliOS stack onto the Return
- stack. In effect you can use the Return stack as a kind of temporary
- storage data structure, and this is far faster and more efficient than
- using complicated HeliOS stack manipulations or using storage variables.
-
- The Return stack is a simple linear "last-in-first-out" structure, just
- like the data stack, so the rules for manipulating it are just like the
- ones you are used to using for the main HeliOS stack. Remember however
- that whatever numbers you store on the Return stack must be removed again
- before you get to the end of the current word definition: you cannot use
- the Return stack to pass arguments from one word to another.
-
- To see how the Return stack might be used to make parameter manipulation
- easier, imagine you have to write a short routine to solve the following
- equation:
-
- ax² + bx + c
-
- Assume that you start with all four parameter values on the stack in the
- following order:
-
- ( a b c x - - - )
-
- First you might factor out the equation as:
-
- ax² + bx + c = x(ax+b)+c
-
- Now look at one way of breaking down the required operation:
-
- OPERATOR DATA STACK RETURN STACK
-
- a b c x
-
- >R a b c x
-
- SWAP ROT c b a x
-
- I c b a x x
-
- * c b ax x
-
- + c (ax+b) x
-
- R> * c x(ax+b)
-
- + x(ax+b)+c
-
-
- Here is this sequence presented as a word definition:
-
- : QUADRATIC ( a b c x -- n )
-
- >R
- SWAP ROT
- I
- *
- +
- R> *
- +
- ;
-
- Now test it:
-
- 2 7 9 3 QUADRATIC
-
- Result -> 48
-
- Perhaps you would like to try to solve this programming task using your
- own new method to see if you can do it more efficiently.
-
- Why not try to do it:
-
- 1. Without using the return stack.
-
- 2. Using the Return stack, but in a different way.
-
-
- ---------------------
- Using HeliOS includes
- ---------------------
-
- The documentation for the Amiga operating system uses a massive number of
- symbolic values, and it is quite impractical to do extensive programming of
- the operating system without using these symbols. If you try to work by
- using directly coded numeric values for all the Amiga symbols you will end
- up making many mistakes and your code will be clumsy and unreadable.
-
- In most languages you have to use the Amiga "Include files" to create an
- on-line reference system for all the symbolic values: then you can type
- the symbolic names in your code rather than numeric values. This is often
- implemented in a very slow and memory-greedy manner, and many programmers
- prefer to define symbolic values directly in their own code to avoid having
- the overhead of using the include files.
-
- HeliOS has an ideal solution to this problem, with a compact, efficient,
- and ultra-fast "precompiled include file" system.
-
- See the documentation file "HeliOS:Docs/UserInterface/IncludeFiles.doc"
- for details of how the HeliOS include system works.
-
- Setting up the HeliOS "high-speed" precompiled include file system is so
- easy, and this system is so fast in operation, that you should ALWAYS use
- it: then you can include ANY Amiga symbol directly in your code by simply
- typing its symbolic name.
-
- You can configure the main HeliOS Interpreter/Editor system to start up
- with default include files already installed automatically, and this is
- recommended for everyday programming work.
-
- However, if you want your programs to be compiled also from the stand-alone
- HeliOS compiler you need to include specific lines in the program code to
- load the include files during compilation, like this:
-
- \ *************************
- \ Load include symbol files
- \ *************************
- \
- \ Note: these "include files" are pre-compiled (for speed) versions of
- \ the full Amiga includes and the HeliOS system includes.
- \
- \ Uncomment the lines below for standalone compilation, but otherwise
- \ it is quicker to set these include files from the HeliOS menus
-
- AMIGAINCLUDE HeliOS:HeliOS_AmigaInclude
- USERINCLUDE HeliOS:HeliOS_UserInclude
-
- Notice how easy it is to use the two words AMIGAINCLUDE and USERINCLUDE to
- set up the include system to load any specified precompiled include files.
-
- Once the include files are set up, you can easily get symbolic values and
- add new symbols from within the main HeliOS Interpreter/Editor program.
-
- From within your program source code or the HeliOS interpreter command line
- you can access any Amiga symbol by simply typing its name.
-
- However there is one important thing to remember:
-
- * The HeliOS include system can be set to return either 16-bit or 32-bit
- values on the HeliOS stack.
-
- To set the HeliOS include system to return 16-bit values you use the word
- WORDINCLUDE.
-
- To set the HeliOS include system to return 32-bit values you use the word
- LONGINCLUDE.
-
- The default setting is always for 32-bit include values, and it is best to
- avoid confusion and possible errors by always returning the system to this
- default status as soon as you have finished using any 16-bit include values.
-
- For example:
-
- INTUBASE \ Put on the stack the 16-bit address
- \ where the 32-bit pointer to Intuition
- \ library base is stored
-
- WORDINCLUDE \ Set include system to return 16-bit values
-
- _LVOViewAddress \ Put on the stack the offset of the library
- \ call "ViewAddress" as a 16-bit value
-
- LONGINCLUDE \ Reset include system to default 32-bit setting
-
- LIBRARY \ Call Intuition Library function to get
- \ VIEW pointer
-
- D0RESULT \ Get dummy register D0 value which is the
- \ View address returned from the library call
-
-
- Try experimenting with the include system at the command line in the HeliOS
- interpreter until you are confident about the way the system works and in
- particular the use of WORDINCLUDE and LONGINCLUDE.
-
-
- -----------------------
- Calling Amiga libraries
- -----------------------
-
- This is very easy in HeliOS, and you should consult the "Dictionary.doc"
- for a full explanation: however, here we will include an example so that
- you can see the direct relationship between an assembly language library
- call and one done in HeliOS.
-
- Here are two examples of the same library call, one in assembler and the
- other in HeliOS:
-
- Assembler:
-
- DOSBASE dc.l 0 ; Initialised to DOS library base by startup code
-
- CONFILE dc.l 0
-
- CON dc.b 'CON:0/12/640/200/HeliOS',0
-
- OPENCON
- move.l #CON,D1
- move.l #MODE_NEWFILE,D2
- move.l DOSBASE,A6
- jsr _LVOOpen(A6)
- move.l D0,CONFILE
- rts
-
- HeliOS:
-
- CREATEL CON <$ CON:0/12/640/200/HeliOS$
-
- 0. DVARIABLE CONFILE
-
- : OPENCON
-
- CON 1 DREG D!
- MODE_NEWFILE 2 DREG D!
- DOSBASE
- WORDINCLUDE _LVOOpen LONGINCLUDE LIBRARY
- D0RESULT CONFILE D!
- ;
-
-
- Now compare them line by line:
-
- : OPENCON -> OPENCON
-
- CON 1 DREG D! -> move.l #CON,D1
-
- MODE_NEWFILE 2 DREG D! -> move.l #MODE_NEWFILE,D2
-
- DOSBASE -> move.l DOSBASE,A6
-
- WORDINCLUDE _LVOOpen LONGINCLUDE LIBRARY -> jsr _LVOOpen(A6)
-
- D0RESULT CONFILE D! -> move.l D0,CONFILE
-
- ; -> rts
-
-
- Notice that HeliOS library calls directly mirror the way things are done
- in assembly language, so you may easily adapt any information you find in
- documentation or examples which refer to assembly language library calls.
-
-
- As a final example, look at the alternative HeliOS version using the word
- "LIBRARYL" instead of "LIBRARY":
-
- : OPENCON
-
- CON 1 DREG D!
- MODE_NEWFILE 2 DREG D!
- _LVOOpen
- DOSBASE LIBRARYL
- D0RESULT CONFILE D!
- ;
-
-
- Notice two advantages here:
-
- 1. We did not need to use WORDINCLUDE and LONGINCLUDE
-
- 2. We have the function offset followed by the line "DOSBASE LIBRARYL", and
- this could conveniently be made into a predefined colon definition which
- would be applicable to ALL our DOS library calls.
-
- We could make a word CALLDOS, like this:
-
- : CallDOS DOSBASE LIBRARYL ;
-
- Then we could say things like:
-
- _LVOOpen CallDOS
- _LVOClose CallDOS
- _LVORead CallDOS
-
- etc.
- etc.
-
- You can see from this example that careful structuring of HeliOS code can
- improve functionality and readability.
-
-
- --------------------------
- Setting up loops in HeliOS
- --------------------------
-
- We have already looked at the use of IF...ELSE...THEN constructs to set up
- conditional branching, and now we are going to learn how to implement loops
- in HeliOS.
-
- Loops are methods of repeatedly executing certain sections of code, and
- broadly speaking, there are three kinds of loop construct:
-
- 1. Counted loops which execute the loop code a predetermined number of times.
-
- 2. Conditional loops which execute the loop code until a condition is met.
-
- 3. Infinite loops which loop forever.....or almost....
-
-
- -------------
- Counted Loops
- -------------
-
- Let us first look at counted loops, which are probably the most common.
-
- In HeliOS, counted loops are set up by specifying the "initial" and "limit"
- values of a "loop index". This loop index is given its initial value for
- the first iteration of the loop, then each time the loop executes the loop
- index is increased until it reaches the limit value, at which point the loop
- finishes.
-
- The loop index is incremented and checked against the loop limit each time
- around at the END of the loop. If the index is still LESS THAN the limit,
- the loop will branch back to the start, but if the index is GREATER THAN
- OR EQUAL TO the limit, the loop finishes and the code carries on past the
- loop end point.
-
- The HeliOS words which implement a simple counted loop are "DO" and "LOOP",
- where DO specifies the start of the loop and LOOP specifies the loop-back
- point. The loop index and loop limit are provided as parameters to the
- word "DO". The loop index and loop limit can be read onto the stack while
- a loop is executing, using the HeliOS words "I" and "I'" respectively.
-
- Here are the definitions of DO and LOOP:
-
- DO ( n1 n2 _ _ _ )
-
- Where:
-
- n1 = loop limit
- n1 = loop index
-
- Used in colon definitions to mark the start of and set up
- a loop.
-
- The loop limit n1 is pushed onto the return stack
- followed by the loop index initial value n2.
-
- The loop executes until the index is incremented by
- LOOP to a value equal to or greater than the loop limit.
-
- LOOP ( _ _ _ )
-
- Used in colon definitions to mark the end of a loop.
-
- The loop index is incremented and execution returns to
- DO if index is still less than limit.
-
- Note that a DO....LOOP construct MUST be defined within a colon definition.
-
- Using this information we can construct a test word to enable us to examine
- the behaviour of a loop in action.
-
- : LOOPTEST
-
- CR
-
- 10 0 \ Loop limit followed by start index (note the order!)
- DO
- I . I' . CR \ Print loop index followed by loop limit
- LOOP
- ;
-
- LOOPTEST
-
- This will display the following results:
-
- 0 10
- 1 10
- 2 10
- 3 10
- 4 10
- 5 10
- 6 10
- 7 10
- 8 10
- 9 10
-
- Notice that the loop index never reaches the value of the loop limit while
- within the loop, because, as soon as the loop index equals the limit, the
- loop ends. As we said earlier, it is important to remember that the index
- is incremented and compared to the loop limit AT THE END of each loop.
-
- Look closely at the example and read the notes above until you are sure
- about the way these simple counted loops work.
-
- It is very important to really understand the mechanism of loops, so here
- again is a step by step breakdown of what happens as HeliOS executes a loop.
-
- 1. The word "DO" removes the limit and index parameters from the HeliOS
- stack and stores them on the Return stack (with the index on top of the
- Return stack and the limit in second place).
-
- 2. HeliOS executes sequentially the words within the loop, up to the word
- "LOOP".
-
- 3. The word "LOOP" increments the loop index (on top of the Return stack)
- and then performs the following checks.
-
- * If the loop index is still less than the loop limit (second on the
- Return stack) the loop returns execution to immediately after "DO"
- and the loop repeats again.
-
- * If the loop index is greater than or equal to the loop limit the loop
- finishes.
-
- 4. When the loop finishes, the index and limit values are removed from
- the Return stack and code execution continues with the word following
- the word "LOOP".
-
- Note that now you have information on how the loop works, you can trick the
- loop mechanism by modifying the loop index and limit values on the Return
- stack from within the loop if you wish. This can give rise to many useful
- techniques, and is perfectly safe once you know what you are doing.
-
- You can set up loops with any range provided that the limit is higher than
- the index, and remember that the index does not have to start at "0".
-
- Look at this interesting example:
-
- : NEGLOOP -230 -240 DO I . LOOP ;
-
- NEGLOOP
-
- will print out:
-
- -240 -239 -238 -237 -236 -235 -234 -233 -232 -231
-
- Notice that the index (-240) still starts as less than the limit (-230).
-
- In many FORTH versions, if you specify the "DO" parameters such that the
- index is greater than or equal to the loop limit at the start, the loop
- will set off and recycle through the whole range of index values until it
- equals the limit again.
-
- HeliOS does not display this uncivilised behaviour, and will merely quit at
- the end of the first loop if you make a mistake like this.
-
- This brings us to an important point:
-
- * No matter what the starting parameters, a HeliOS loop will ALWAYS go
- through the intermediate code (or "body") of the loop at least once.
-
- This is because no checking of the index amnd limit values is carried out
- until the end of the loop is reached.
-
- Another somewhat subtle point is that you can only use the words "I" and
- "I'" to get the loop index and limit within the SAME colon definition in
- which the loop is defined. In other words, you cannot use "I" and "I'"
- in any sub-words within the loop.
-
- For example, the following code would not work correctly:
-
- : PRINTINDEX I . ;
-
- : TESTLOOP
-
- 10 0
- DO
- PRINTINDEX
- LOOP
- ;
-
- The reason that this would fail is that the loop parameters are only at
- the top of the Return stack in the "root" level of the colon definition
- in which the loop is defined. When HeliOS enters the word PRINTINDEX in
- the above example the Return stack is used to store "Return" information
- by the HeliOS system, so the words "I" and "I'" will no longer return the
- actual loop parameters from the Return stack.
-
- Of course, you can have loops within loops, and these are called "Nested
- Loops". Nested loops are just like ordinary loops, but there is a useful
- way of getting at the loop parameters of an outer nested loop from inside
- the inner nested loop.
-
- This is done by using special Return stack operators ("J", "K" etc.) which
- get the stack values for outer loops from BELOW the top two stack items.
-
- For example, here are some useful Return stack operators to use in nested
- loops:
-
- I ( _ _ n1 ) Get copy of top number on return stack.
- ( = 1st loop index. )
- I' ( _ _ n1 ) Get copy of second number on return stack.
- ( = 1st loop limit. )
- J ( _ _ n1 ) Get copy of third number on return stack.
- ( = 2nd loop index. )
- J' ( _ _ n1 ) Get copy of fourth number on return stack.
- ( = 2nd loop limit. )
- K ( _ _ n1 ) Get copy of fifth number on return stack.
- ( = 3rd loop index. )
- K' ( _ _ n1 ) Get copy of sixth number on return stack.
- ( = 3rd loop limit. )
-
- Here is an interesting example of a nested loop:
-
- : NESTLOOP
-
- SCRCLR
- 0
- 400 100
- DO
- 1+ DUP GFXSETAPEN
- 150 100
- DO
- J I GFXWRITE
- LOOP
- LOOP
- WAITSPACE
- ;
-
- NESTLOOP
-
-
- Other useful function to use in counted loops are "/LOOP" and "+LOOP":
- these words allow you to specify increments other than "1" for the loop
- index for each loop cycle.
-
- Here are the definitions of these words:
-
- +LOOP ( n1 _ _ _ )
-
- Used in colon definitions in the form:
-
- DO - - - - - n1 +LOOP
-
- to implement a loop in which the index is incremented by
- n1 each time the loop executes.
-
- The branch back to DO occurs as long as the index remains
- less than the limit if n1 > 0, or for as long as it is
- greater than the limit if n1 < 0.
-
- Note than n1 can be positive or negative for this word.
-
- /LOOP ( n1 _ _ _ )
-
- Used in colon definitions in the form:
-
- DO - - - - - n1 /LOOP
-
- to implement a loop in which the index is incremented by
- n1 each time the loop executes.
-
- The branch back to DO occurs as long as the index remains
- less than the limit.
-
- In this case n1 must always be positive (unsigned) and
- the loop index can exceed 32767.
-
- For example:
-
- : PLUSLOOP 10 0 DO I . 2 +LOOP ;
-
- PLUSLOOP
-
- will give us:
-
- 0 2 4 6 8
-
- Now look at these more interesting examples:
-
-
- : NEGLOOP1 10 0 DO I . -2 +LOOP ;
-
- NEGLOOP1
-
- will give us:
-
- 0
-
- : NEGLOOP2 0 10 DO I . -2 +LOOP ;
-
- NEGLOOP2
-
- will give us:
-
- 10 8 6 4 2 0
-
- : NEGLOOP3 -10 0 DO I . -2 +LOOP ;
-
- NEGLOOP3
-
- will give us:
-
- 0 -2 -4 -6 -8 -10
-
- Look at these examples and try to follow the way the loop index and limit
- are being used to control the loop.
-
- Here is another interesting example:
-
- : DOUBLE-LOOP CR 200 1 DO I . I +LOOP ;
-
- Here the index itself is used as the increment so that starting with
- one the index doubles each time:
-
- 1 2 4 8 16 32 64 128 256 etc.
-
- Note that in this example we did not set the loop index to start at "0".
-
- Can you see why?
-
- Yes, if we had done so the argument for +LOOP would have been "0", and in
- this case we would have created an infinite loop because the loop index
- would never change.
-
-
- -----------------
- Conditional Loops
- -----------------
-
- Conditional loops repeat until a certain condition is met, and are created,
- at their simplest, by using the "BEGIN...UNTIL" construct.
-
- We say "at their simplest" because there are a few more complex variants
- such as BEGIN...WHILE...REPEAT.
-
- Here are a few word definitions:
-
- BEGIN ( _ _ _ )
-
- Used in colon definitions in the forms :
-
- * BEGIN - - - - - UNTIL
-
- Repeats if top stack value is "0" at UNTIL
-
-
- * BEGIN - - - - - AGAIN
-
-
- Repeats always
-
- * BEGIN - - - - - WHILE - - - - - REPEAT
-
- If WHILE finds a 0, it diverts execution to beyond
- REPEAT, leaving the conditional structure.
-
- If flag for WHILE is "true", execution is allowed to
- continue through to REPEAT and then back to BEGIN.
-
- BEGIN marks the start of a repeating section of code and
- a return point for UNTIL, AGAIN, and REPEAT.
-
-
- REPEAT ( _ _ _ )
-
- Used in colon definitions to force execution to return to
- the point just after the word BEGIN.
-
-
- UNTIL ( flag _ _ _ )
-
- Used in colon definitions to control a conditional branch
- back to BEGIN.
-
- If flag is false execution reverts to BEGIN, but if true
- execution continues ahead.
-
-
- WHILE ( flag _ _ _ )
-
- Used in colon definitions to implement
-
- BEGIN - - - WHILE - - - REPEAT
-
- conditional structures.
-
- If flag is "false", WHILE diverts execution to beyond
- REPEAT, leaving the conditional structure.
-
- If flag is "true", execution is allowed to continue
- through to REPEAT and then back to BEGIN.
-
-
- Looking at the simple BEGIN...UNTIL loop, we can see that it is a very
- simple conditional structure.
-
- Here is an example:
-
- : WAIT-KEY BEGIN ?TERMINAL UNTIL ;
-
- WAIT-KEY
-
- This is so simple that it hardly needs explanation: the loop simply cycles
- until the value left on the stack by ?TERMINAL is non-zero, then execution
- continues from after UNTIL.
-
- Although BEGIN...UNTIL loops are very simple, they are very powerful and
- useful. Sometimes, however, you need a slightly more powerful version of
- the conditional loop, and this is provided by the BEGIN...WHILE...REPEAT
- loop construct.
-
- In this variant of the conditional loop, the "clever bit" is performed by
- the word WHILE, which acts according to a flag which it receives on the
- stack. If this flag is "false", WHILE diverts execution to beyond REPEAT,
- leaving the conditional structure. If the flag is "true", execution is
- allowed to continue through to REPEAT and then back to BEGIN.
-
- Here is an example:
-
- : WAIT-REPEAT
-
- SCRCLR
- 20 8 CURPUT
- ." I'm waiting for a key!"
-
- BEGIN
- BELL
- ?TERMINAL 0=
- WHILE
- ." ."
- 5 DELAY
- REPEAT
-
- SCRCLR
-
- 20 8 CURPUT
- ." Thank You!"
-
- 20 15 CURPUT
- ." Please press <Space> to continue...."
-
- WAITSPACE
- ;
-
- WAIT-REPEAT
-
- --------------
- Infinite Loops
- --------------
-
- The final form of loop construct is the infinite loop, and this can be set
- up using BEGIN AGAIN.
-
- As you might expect, this type of loop is fairly permanent, and will simply
- cycle around for ever.....
-
- Actually you can exit from an infinite loop by using a word such as ABORT,
- or by using SYS@ and SYS!. However, these techniques do require a little
- experience, and are only mentioned here for the sake of completeness.
-
- See the Dictionary documentation for more details.
-
- *************************************************************************
- End
- *************************************************************************
-